﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Web.Script.Serialization;
using Myotragus.Data;
using Myotragus.Data.Containers;
using Myotragus.Data.Domain;
using Myotragus.Data.Emit;
using Myotragus.Data.Hbm;
using Myotragus.Data.Registration;
using NHibernate;
using NHibernate.ByteCode.Castle;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Tool.hbm2ddl;
using NLipsum.Core;
using System.Linq;
using Environment = NHibernate.Cfg.Environment;

namespace Myotragus.Store.Model.Tests
{
  public static class FixtureHelper
  {
    public static IServicesContainer Container
    {
      get;
      private set;
    }

    public static void InitializeServices(params Type[] registerFirst)
    {
      InitializeServices((IEnumerable<Type>)registerFirst);
    }

    public static void InitializeServices(IEnumerable<Type> registerFirst)
    {
      if (Container != null)
        return;

      var container = ContainerHelper.CreateContainer();

      container.Register(ServicesImplementation.FromThese(registerFirst));
      container.Register(ServicesImplementation.FromAssemblyContaining<IEntity>());
      container.Register(ServicesImplementation.FromAssembly(Assembly.GetExecutingAssembly()));

      Container = container;
    }

    private static readonly JavaScriptSerializer Serializer = new JavaScriptSerializer();

    public static string ToJsString(object obj)
    {
      return Serializer.Serialize(obj);
    }
    public static void Print(object obj)
    {
      Console.WriteLine(ToJsString(obj));
    }

    public static IEnumerable<string> LstCultureNames = new[] { "es", "en", "it" };

    public static IDictionary<string, CultureInfo> DicCultures = LstCultureNames
      .Concat(LstCultureNames.Select(c => c.ToUpper()))
      .ToDictionary(c => c, c => CultureInfo.GetCultureInfo(c));

    public static IDictionary<string, string> DicLipsums = new[]
    {
      new KeyValuePair<string, string>("es", Lipsums.TierrayLuna),
      new KeyValuePair<string, string>("ES", Lipsums.TierrayLuna),
      new KeyValuePair<string, string>("en", Lipsums.TheRaven),
      new KeyValuePair<string, string>("EN", Lipsums.TheRaven),
      new KeyValuePair<string, string>("de", Lipsums.InDerFremde),
      new KeyValuePair<string, string>("DE", Lipsums.InDerFremde),
      new KeyValuePair<string, string>("fr", Lipsums.LeBateauIvre),
      new KeyValuePair<string, string>("FR", Lipsums.LeBateauIvre),
      new KeyValuePair<string, string>("it", Lipsums.Decameron),
      new KeyValuePair<string, string>("IT", Lipsums.Decameron),
    }.ToDictionary(p => p.Key, p => p.Value);

    private static Configuration _configuration;

    public static int GetLcId(string cultureName)
    {
      return DicCultures[cultureName].LCID;
    }

    public static string GenerateSentence(int count, string culture)
    {
      return LipsumGenerator.Generate(count, Features.Sentences, "{0}", DicLipsums[culture]);
    }

    public static string GenerateParagraph(int count, string culture)
    {
      return LipsumGenerator.Generate(count, Features.Paragraphs, "{0}", DicLipsums[culture]);
    }

    public static ISessionFactory CreateSqlLiteDbFactory(IEnumerable<string> hbms)
    {
      var cfg = new Configuration()
        .SetProperty(Environment.ReleaseConnections, "on_close")
        .SetProperty(Environment.ShowSql, "true")
        .SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
        .SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
        .SetProperty(Environment.ConnectionString, "data source=test.md")
        .SetProperty(Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName);

      if (hbms != null)
        foreach (var hbm in hbms)
          cfg.AddXml(hbm);

      var sessionFactory = cfg.BuildSessionFactory();

      using (var session = sessionFactory.OpenSession())
        new SchemaExport(cfg).Execute(true, true, false, session.Connection, null);

      return sessionFactory;
    }

    public static void PrintType(Type type)
    {
      var result = String.Format("class {0} {{", type.FullName);
      result = type.GetProperties().Aggregate(result, (current, p) 
        => current + String.Format("\r\n\t{1} {0} {{ {2} }}\r\n", 
          p.Name, p.PropertyType, p.CanWrite ? "get;set;" : "get;"));
      result += "}";

      Console.WriteLine(result);
    }

    public static void InitializeEntities(IEnumerable<Type> entities)
    {
      var generators = new[]
      {
        typeof(IAutoProperties),
        typeof(ILoadEntityGenerator),
        typeof(ISaveEntityGenerator),
        typeof(IDefaultsLoadEntityGenerator),
        typeof(IDefaultsSaveEntityGenerator),
      };

      var registrarType = Container.Resolve<IEntitiesRegistrar>();

      foreach (var type in generators
        .Select(genType => (ITypeGenerator)Container.Resolve(genType))
        .SelectMany(genType => entities.Select(t => genType.GetImplementation(t))))
      {
        registrarType.RegisterForAll(type);
      }
    }

    public static void InitializeSession(IEnumerable<Type> entities)
    {
      var generatorTypes = new[]
      {
        new {Type = typeof(ILoadEntityGenerator), Hbm=typeof(ILoadEntityHbmGenerator)},
        new {Type = typeof(ISaveEntityGenerator), Hbm=typeof(ISaveEntityHbmGenerator)},
        new {Type = typeof(IDefaultsLoadEntityGenerator), Hbm=typeof(IDefaultsLoadEntityHbmGenerator)},
        new {Type = typeof(IDefaultsSaveEntityGenerator), Hbm=typeof(IDefaultsSaveEntityHbmGenerator)},
      };

      _configuration = new Configuration();
      _configuration.Configure();
      _configuration.AddAssembly(typeof(ILocalizableText).Assembly);

      foreach (var pair in generatorTypes)
      {
        var genType = (ITypeGenerator)Container.Resolve(pair.Type);
        var genHbm = (IHbmGenerator)Container.Resolve(pair.Hbm);

        foreach (var ent in entities)
        {
          _configuration.AddAssembly(ent.Assembly);

          var type = genType.GetImplementation(ent);
          _configuration.AddXml(genHbm.GenerateMapping(type, ent));
        }
      }

      var factory = Container.Resolve<ISessionFactoryAdapter>();
      factory.SetFactory(_configuration.BuildSessionFactory());
    }

    public static void AddMappings(string xml)
    {
      _configuration.AddXml(xml);
      var factory = Container.Resolve<ISessionFactoryAdapter>();
      factory.SetFactory(_configuration.BuildSessionFactory());
    }
  }
}